#include "bruteforce.h"
#include <iostream>

BruteForce::BruteForce() //: last_sig_id(0)
{
}

BruteForce::~BruteForce()
{
}

bool BruteForce::start()
{
//    double prev_error = psignal_study_forward->geterror();
    ntype::error_t err_before = MAX_ERROR_VALUE, err_after = MIN_ERROR_VALUE;
    ntype::error_t prev_error = MAX_ERROR_VALUE;
    calcStudyError(err_before, prev_error);
    uint32_t cicle_count = 0;
    ntype::error_t error_after_study = MAX_ERROR_VALUE;
std::cout << "########### START STUDY [error: "<< err_after << "]###########" << std::endl;
//std::cout << "RETURN FOR DEBUG" << std::endl;
    std::cout << " max allowed error: "<< max_study_error << std::endl;
    for(; cicle_count < study_cicle_count_max; cicle_count ++)
    {
//std::cout << "Study cicle count: " << cicle_count << " max allowed cicle count: "<< study_cicle_count_max << std::endl;
      error_after_study = study4OneCicle();
      if(error_after_study <= max_study_error)
        break;
      else if(prev_error <= error_after_study)
      {
         std::cout << "Study["<< cicle_count <<"] Error was not changed to better value: before study: "<< prev_error << " after study: " << error_after_study << " max allowed: "<< max_study_error <<std::endl;
         break;
      }
std::streamsize p = std::cout.precision();
std::cout.precision(19);
std::cout << "Error before study: "<< prev_error << " Error after study: " << error_after_study <<std::endl;
std::cout.precision(p);
//std::cout <<std::endl;

    prev_error = error_after_study;
    }
std::cout << "########### FINISH STUDY [count: "<< cicle_count <<"] err after study: " << error_after_study << " ###########" << std::endl;
return true;
}


double BruteForce::study4OneCicle()
{
 std::cout << "--------- start study 1 cicle" << std::endl;

    neuron neuron_tmp = pneuron[0];
    ntype::error_t err_before = -1.0, err_after;
    ntype::synapsecount_t synapse_count_max = neuron_tmp.getSynapseCount();
    for(ntype::neuronid_t nid_count = 0; nid_count < neuron_count_max; nid_count ++)
    {
        for(ntype::synapsecount_t syn_count = 0; syn_count < synapse_count_max; syn_count ++)
        {
            correctNeuron(nid_count, syn_count);
        }
    }
    calcStudyError(err_before, err_after);
std::cout << "------------- finish study 1 cicle" << std::endl;
    return err_after;
}

bool BruteForce::calcStudyError(ntype::error_t &err_before, ntype::error_t &err_after)
{
   err_after = 0.0;
   ntype::signalid_t signal_count_max = 0;
   for(ntype::signalid_t sig_id = 0; sig_id < study_in_signal.size(); ++sig_id)
   {
       signal_count_max ++;
       psignal_study_forward->setstudysignalid(sig_id);
       psignal_study_forward->send(null, 0);
       double err_value = psignal_study_forward->geterror(study_in_signal[sig_id]);
//std::cout << "sinal_id: " << sig_id << " err val: " << err_value << std::endl;
       err_after += err_value;
   }
   err_after /= signal_count_max;
//std::cout << "err before: " << err_before << " err after: " << err_after << std::endl;
   return (err_before > err_after) ? true : false;
}

void BruteForce::correctNeuron(ntype::neuronid_t nid, ntype::synapseid_t sid)
{
   neuron &neuron_tmp = pneuron[nid];
   synapse &syn = neuron_tmp.getSynapse(sid);
   bool isPlus = true;
   int iCountMax = 2, iCount = 0;
   const ntype::weight_t orig_weight = syn.getWeight();
   ntype::error_t ebefore = 0.0, efake = 0.0;
   ntype::error_t eafter = 0.0;
   calcStudyError(efake, ebefore);
   ntype::weight_t wbefore = syn.getWeight();

   /**
     при 10 слоях и 2-х сигналах изменения на 0,001 мало,
     надо увеличить точность(разрядность вычисления),
     но с увличением разрядности увеличиваетися время обучения.
     */

//   static const double d_delta = 0.000001;
   static const double d_delta = 0.000001;
   syn.setWeight(orig_weight + orig_weight*d_delta);
   calcStudyError(ebefore, eafter);
   if(eafter > ebefore)
   {//revert changes
     syn.setWeight(orig_weight - orig_weight*d_delta); //0.000001
     calcStudyError(ebefore, eafter);
     if(eafter > ebefore)
     {
         syn.setWeight(orig_weight);
         std::cout << "revert the latest changes, error was not decresed" << std::endl;
         std::cout << "revert data to previouse state: nid: "<< nid << " sid: " << sid << " w.value: "<< syn.getWeight() << std::endl;
     }
   }

#if 1
//   calcStudyError(ebefore, eafter);
   std::cout << "nid->: "<< nid <<
                " sid: " << sid <<
                " wdelta: "<< d_delta <<
                " wbefore: "<< wbefore <<
                " wafter: "<< syn.getWeight() <<
                " error before: "<< ebefore <<
                " error after: " << eafter <<
                std::endl;
#endif
#if 0
   do {
     if(isPlus)
     {
       syn.setWeight(syn.getWeight() + syn.getWeight()*0.0001);
     } else
     {
       syn.setWeight(syn.getWeight() - syn.getWeight()*0.0001);
     }

     if(calcStudyError(ebefore, eafter) == true)
     {
#if 0
std::cout << "nid->: "<< nid <<
             " sid: " << sid <<
             " wbefore: "<< wbefore <<
             " wafter: "<< syn.getWeight() <<
             " error before: "<< ebefore <<
             " error after: " << eafter <<
             std::endl;
#endif
        break;
     } else
     {
         syn.setWeight(orig_weight);
         isPlus = false;
     }
     iCount ++;
   } while(iCount < iCountMax);
#endif
//calcStudyError(ebefore, eafter);
//std::cout << "nid: "<< nid << " sid: " << sid << " w: " << pneuron[nid].getSynapse(sid).getWeight() << " error before: "<< ebefore << " error after: " << eafter << std::endl;

}
